/*
 * Copyright 2013 Robert Peszek.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package fpig.common.functions

import fpig.common.Either;
import fpig.common.Just;
import fpig.common.Left;
import fpig.common.Maybe;
import fpig.common.Nothing;
import fpig.common.Right;
import fpig.concepts.MonadDescription;
import fpig.groovylists.functions.InAndOutOfFunLists;
import fpig.lists.functions.FunListMonad;
import fpig.lists.structures.FunList;
import fpig.lists.structures.LazyList
import fpig.monad.functions.BaseM;
import fpig.util.CallUtil
import fpig.util.FunUtil;
import fpig.util.curring.Parameter_;
import groovy.lang.Closure;

/**
 * Collections of base functions and methods.
 * <p>
 * FpigBase defines {@code f} transform for converting Groovy concepts to functional concepts (currently works with Lists and Closures).
 * As well as a set of 'polymorphic' functions (curried closures) which work across several functional types:
 * <pre>
 *  b - bind working for all monads defined within Fpiglet
 *  unpure - works for all monads defined within Fpiglet
 * </pre> 
 * FpigBase defines functions for interacting with {@link Maybe}
 * <pre>
 *  Maybe maybe4 = just(4) //wraps 4 in Maybe context
 *  Maybe maybeNothing = nothing() //returns Nothing version of Maybe
 *  unjust(maybe4) //unwraps 4
 *  isSomething(maybe4) //checks if Maybe is Just or Nothing
 *  unpure(maybe4) {
 *    assert it==4
 *  }  //note unpure serves as generalized version of unjust
 * </pre> 
 * FpigBase defines functions for interacting with {@link Either}
 * <pre>
 * Either success = right(5) //wraps 5 in Either context
 * Either error = left('this is wrong')
 * assert isRight(success)
 * assert !isRight(error)
 * assert 5 = unright(success)
 * unpure(success){
 *   assert it == 5
 * } //note unpure serves as generalized version of unright
 * </pre> 
 * 
 * Other conveniences include '_' definintion, as well as flip and apply functions.
 *   
 * @see Either
 * @see Maybe  
 * @see MonadDescription
 * @see <a href="http://code.google.com/p/fpiglet/">http://code.google.com/p/fpiglet/</a>
 * @author Robert Peszek
 */
class FpigBase {
	
	/**
	 * Converts Closure to Curried Function.
	 * <p>
	 * One of very few methods in Fpiglet. It is a method to take advantage of method overloading.
	 *  
	 * @see CallUtil
	 * @see <a href="http://code.google.com/p/fpiglet/">http://code.google.com/p/fpiglet/</a>
	 * @see <a href="http://code.google.com/p/fpiglet/wiki/CurriedFunctions">http://code.google.com/p/fpiglet/wiki/CurriedFunctions</a>
	 * @param c - Groovy closure
	 * @return - curried function version of the closure.
	 */
	static Closure f(Closure c) {
		CallUtil.toFunction(c)
	}
	
	/**
	 * Converts Groovy List ot functional FunList
	 * <p>
	 * One of very few methods in Fpiglet. It is a method to take advantage of method overloading.
	 * 
	 * @see FunList 
	 * @param l - List
	 * @return FunList
	 */
	static FunList f(List l) {
		(InAndOutOfFunLists.funlistIn << l)
	}

	/**
	 * Placeholder parameter used in defining partially applied curried functions.
	 * <p>
	 * Useful if program needs to partially apply parameter wich is not first on the list.
	 * <p>
	 * Example:
	 * <pre>
	 *  POWER(_,2) //squares numbers
	 * </pre>
	 */
	static def _ = CallUtil._

	/**
	 * Flips first 2 function arguments.
	 * <p>
	 * Example 
	 * <pre>
	 * def c = {a, b -> a}
	 * assert c(2,5) == 2 
	 * assert flip(c)(2,5) == 5 
     * </pre>
     * Logical signature:
     * <pre>
     *   (a->b) -> b->a
     * </pre>
	 * @see CallUtil
	 */
	static flip = CallUtil.flip

	/**
	 * Reversed function application.
	 * <p>
	 * What is 5, is 5 a function?  Well if f(5) produces a value then who is to say that f is a function and 5 is a value?
	 * Maybe it is the other way around?  {@code apply} make this happen:
	 * <pre>
	 *   apply(5,f) //is the same as f(5)
	 *   def function5 = apply(5)
	 *   function5(f) //is the same as f(5)
	 * </pre>
	 * 
	 * @see CallUtil
	 */
	static Closure apply = f(CallUtil.functionApplicationFlipped)

	/**
	 * Identity Closure.
	 * <pre>
	 *  ID(x) //same as x
	 * </pre>
	 */
	static Closure ID = Closure.IDENTITY

	/**
	 * Polymorphic version of monadic bind.
	 * <p>	
	 * Currently works only on Fpiglet built-in monads. If you define your own, it will not work.
	 * <p>
	 * Logical signature same as monadic bind.
	 * @see MonadDescription
	 */
	static Closure getB(){
		return CallUtil.toFunction {Closure c,  m-> 
		   MonadDescription monad = getMonad(m)
		   monad.bind(c, m)
	   }		
	}

	/**
	 * Private, returns MonadDescription for given instance of a monadic value.
	 */
	static MonadDescription getMonad(m){
		MonadDescription monad
		if(m instanceof FunList){
			monad = FunListMonad.instance
		} else if (m instanceof Maybe){
			monad = MaybeMonad.instance
		} else if (m instanceof Either){
		   monad = EitherMonad.instance
		} else {
			throw new RuntimeException('No bind implemented for '+ m)
		}
        monad
	}
	
	/**
	 * Polymorphic version unpure monadic method.
	 * <p>	
	 * Currently works only on Fpiglet built-in monads. If you define your own, it will not work.
	 * See {@link BaseM#getUnpure()} for detailed description.
	 * 
	 * @see BaseM#getUnpure()
	 */
	static Closure getUnpure(){
		return CallUtil.toFunction {m, Closure c->
		   MonadDescription monad = getMonad(m)
		   BaseM mf = BaseM.getInstance(monad)
		   mf.unpure(m, c)
	   }
	}

	/**
	 * Flipped version of unpure. Useful when injecting into monadic composition.
	 *
	 * @see BaseM#getUnpure()
	 */
	static Closure getAsUnpure(){
		CallUtil.flip unpure
	}

	/**
	 * Wraps value in Maybe (Just) context.
	 * <p>
	 * Logical signature
	 * <pre>
	 * just:: A -> Maybe<A>
	 * </pre>
	 * {@code T t; just(t)} returns {@code Just<T>}
	 * 
	 * @see Maybe
	 * @see MaybeMonad
	 */
	static Closure just = {val -> new Just(_val:val)}	
	
	/**
	 * Returns Nothing version of Maybe.
	 * <p>
	 * Logical signature
	 * <pre>
	 * unjust:: _ -> Maybe
	 * </pre>
	 * Accepts any number of arguments.
	 * @see Maybe
	 * @see MaybeMonad
	 */
	static Closure nothing = {Object[] args -> Nothing.instance}
	
	/**
	 * Unwraps content of Maybe
	 * <p>
	 * Logical signature
	 * <pre>
	 * unjust:: Maybe<A> -> A
	 * </pre>
	 * will throw RuntimeException if called on Nothing.
	 * @see Maybe
	 * @see MaybeMonad
	 */
	static Closure unjust = {Maybe m->
		if(m instanceof Just){
			m.val
		} else {
		    throw new RuntimeException('Cannot unjust Nothing')
		}
	}
	
	/**
	 * Returns true if Maybe is Just and false if it is Nothing.
	 * <p>
	 * Logical signature
	 * <pre>
	 * Maybe<A> -> boolean
	 * </pre>
	 * @see Maybe
	 * @see MaybeMonad
	 */
	static Closure isSomething = {Maybe m-> 
		(m instanceof Just) 
	}
	
	
	/**
	 * Wraps value in Either context. Typically used to wrap successful result value.
	 * <p>
	 * Logical signature
	 * <pre>
	 *  A -> Either<_, A>
	 * </pre>
	 * @see Either
	 * @see EitherMonad
	 */
	static Closure right = {val -> new Right(_val:val)}
	
	/**
	 * Returns Left Either value. Typically used to return error information.
	 * <p>
	 * Left Could contain error message or Exception as value.
	 * <p>
	 * Logical signature
	 * <pre>
	 *  E -> Either<E, _>
	 * </pre>
	 * @see Either
	 * @see EitherMonad
	 */
	static Closure left = {val -> new Left(_val:val)}
	
	/**
	 * If Either is Right returns true, if is Left returns false. Can be used to check if Either value contains successful result.
	 * <p>
	 * Logical signature:
	 * <pre>
	 *   Either<E,A> -> boolean
	 * </pre>
	 * @see Either
	 * @see EitherMonad
	 */
	static Closure isRight = {Either e-> 
		(e instanceof Right)
	}
	
	/**
	 * Unwraps Right value from Either.
	 * <p>
	 * Will throw RuntimeException if Either is Left.
	 * Logical signature:
	 * <pre>
	 *   Either<E,A> -> A
	 * </pre>
	 * @see Either
	 * @see EitherMonad
	 */
	static Closure unright = {Either e->
		if(e instanceof Right){
			e.val
		} else {
		  throw new RuntimeException('Cannot unRight Left')
		}
	}
	
	/**
	 * Unwraps Left value from Either.
	 * <p>
	 * Will throw RuntimeException if Either is Right.
	 * Logical signature:
	 * <pre>
	 *   Either<E,A> -> E
	 * </pre>
	 * @see Either
	 * @see EitherMonad
	 */
	static Closure unleft = {Either e->
		if(e instanceof Left){
			e.val
		} else {
		  throw new RuntimeException('Cannot unLeft Right')
		}
	}
}
